/**
  * @file	bootloader.c
  *	@author	yoyoyes
  * @date	2023-6-8
  */
  
#include "bootloader.h"
#include "string.h"
#include "systick.h"
#include "usart.h"
#include "M24C02.h"
#include "WHGM5.h"
#include "flag.h"

uint16_t boot_flag;
ota_info_struct ota_info;
update_zone_struct update_zone;
cmd_list_struct boot_cmd_list;


/**
  * @func	bootloader_erase_zone_callback
  * @param	none
  * @return	none
  */
int bootloader_erase_zone_callback(void)
{
	gd32_flash_erase(GD_FLASH_A_BASE_PAGE, GD_FLASH_A_PAGE_NUM);
	usart0_printf("[info] Zone A has been erased!\a\r\n");
	
	return 0;
}

/**
  * @func	bootloader_IAP_download_callback
  * @param	none
  * @return	none
  */
int bootloader_IAP_download_callback(void)
{
	uint16_t len;
	
	system_flag_set(&boot_flag, BOOT_FLAG_IAP_XMODEMC);
	usart0_printf("[info] ");
	
	while(1)
	{
		Xmodem_start_signal();
		
		len = usart_rx_frame_read(&usart0_rx_control_block, usart0_rx_frame);
		if((len == 133) && (usart0_rx_frame[0] == 0x01))
		{
			system_flag_clr(&boot_flag, BOOT_FLAG_IAP_XMODEMC);
			
			update_zone.Xmodem_CRC = Xmodem_CRC16(&usart0_rx_frame[3], 128);
			if(update_zone.Xmodem_CRC == usart0_rx_frame[131] * 256 + usart0_rx_frame[132])
			{
				update_zone.Xmodem_package_num++;
				memcpy(&update_zone.update_buff[((update_zone.Xmodem_package_num - 1) % (GD_FLASH_PAGE_SIZE / 128)) * 128], &usart0_rx_frame[3], 128);
				if((update_zone.Xmodem_package_num % (GD_FLASH_PAGE_SIZE / 128)) == 0)
				{
					gd32_flash_write(GD_FLASH_A_BASE_ADDR + ((update_zone.Xmodem_package_num / (GD_FLASH_PAGE_SIZE / 128)) - 1) * GD_FLASH_PAGE_SIZE, 	\
									(uint32_t *)update_zone.update_buff, GD_FLASH_PAGE_SIZE);
				}
				usart0_printf("\x06");
			}
			else
			{
				usart0_printf("\x15");
			}
		}
		
		if((len == 1) && (usart0_rx_frame[0] == 0x04))
		{
			usart0_printf("\x06");
			
			if((update_zone.Xmodem_package_num % (GD_FLASH_PAGE_SIZE / 128)) != 0)
			{
				gd32_flash_write(GD_FLASH_A_BASE_ADDR + (update_zone.Xmodem_package_num / (GD_FLASH_PAGE_SIZE / 128)) * GD_FLASH_PAGE_SIZE, 	\
								(uint32_t *)update_zone.Xmodem_package_num, (update_zone.Xmodem_package_num % (GD_FLASH_PAGE_SIZE / 128)) * 128);
			}
			
			systick_delay_ms(100);
			NVIC_SystemReset();
		}
	}
}

/**
  * @func	bootloader_version_set_callback
  * @param	none
  * @return	none
  */
int bootloader_version_set_callback(void)
{
	char version_num[10];
	
	usart0_printf("[info] Input bootloader version (for example: 1.2.0)\r\n");
	usart0_printf("[info] ");
	usart0_gets(version_num, sizeof(version_num));
	
	memset(ota_info.ota_fire_version, 0, sizeof(ota_info.ota_fire_version) / sizeof(char));
	memcpy(ota_info.ota_fire_version, version_num, sizeof(version_num) / sizeof(char));
	bootloader_ota_event_flag_save();
	usart0_printf("[info] Version %s set success!\a\r\n", ota_info.ota_fire_version);
	
	return 0;
}

/**
  * @func	bootloader_version_get_callback
  * @param	none
  * @return	none
  */
int bootloader_version_get_callback(void)
{
	bootloader_ota_event_flag_read();
	usart0_printf("[info] bootloader version : %s\a\r\n", ota_info.ota_fire_version);
	
	return 0;
}

/**
  * @func	bootloader_system_reset_callback
  * @param	none
  * @return	none
  */
int bootloader_system_reset_callback(void)
{
	systick_delay_ms(100);
	usart0_printf("[info] System reset...\r\n");
	NVIC_SystemReset();
	
	return 0;
}

/**
  * @func	bootloader_server_info_set_callback
  * @param	none
  * @return	none
  */
int bootloader_server_info_set_callback(void)
{
	//WHGM5_net_rssi_get();
	WHGM5_server_info_set();
	WHGM5_start_info();
	
	return 0;
}

/**
  * @func	bootloader_init
  * @param	none
  * @return	none
  */
void bootloader_init(void)
{
	boot_cmd_list.boot_cmd[0].cmd_name = "1";
	boot_cmd_list.boot_cmd[0].cmd_callback = bootloader_erase_zone_callback;
	
	boot_cmd_list.boot_cmd[1].cmd_name = "2";
	boot_cmd_list.boot_cmd[1].cmd_callback = bootloader_IAP_download_callback;
	
	boot_cmd_list.boot_cmd[2].cmd_name = "3";
	boot_cmd_list.boot_cmd[2].cmd_callback = bootloader_version_get_callback;
	
	boot_cmd_list.boot_cmd[3].cmd_name = "4";
	boot_cmd_list.boot_cmd[3].cmd_callback = bootloader_version_set_callback;
	
	boot_cmd_list.boot_cmd[4].cmd_name = "5";
	boot_cmd_list.boot_cmd[4].cmd_callback = bootloader_system_reset_callback;
	
	boot_cmd_list.boot_cmd[5].cmd_name = "6";
	boot_cmd_list.boot_cmd[5].cmd_callback = bootloader_server_info_set_callback;
	
	/* add new cmd */
}

/**
  * @func	bootloader_ota_event_flag_read
  * @param	none
  * @return	none
  */
void bootloader_ota_event_flag_read(void)
{
	memset(&ota_info, 0, OTA_INFO_STRUCT_SIZE);
	M24C02_read_byte(0, (uint8_t *)&ota_info, OTA_INFO_STRUCT_SIZE);
}

/**
  * @func	bootloader_ota_event_flag_save
  * @param	none
  * @return	none
  */
void bootloader_ota_event_flag_save(void) 
{
	uint8_t i;

	for(i = 0; i < (OTA_INFO_STRUCT_SIZE / 16 + 1); i++)
	{
		M24C02_write_page(i * 16, (uint8_t *)&ota_info + i * 16);
		systick_delay_ms(5);
	}
}

/**
  * @func	Xmodem_CRC16
  * @param	data		数据指针
  * @param 	data_len	数据长度
  * @return	校验值
  */
uint16_t Xmodem_CRC16(uint8_t *data, uint16_t data_len)
{
	uint8_t i;
	uint16_t crc_init = 0x0000;
	uint16_t crc_ipoly = 0x1021;

	while(data_len--)
	{
		crc_init = (*data << 8) ^ crc_init;
		for(i = 0; i < 8; i++)
		{
			if(crc_init & 0x8000)
			{
				crc_init = (crc_init << 1) ^ crc_ipoly;
			}
			else
			{
				crc_init = (crc_init << 1);
			}
		}
		data++;
	}		
	
	return crc_init;
}

/**
  * @func	Xmodem_start_signal
  * @param	none
  * @return	none
  */
void Xmodem_start_signal(void)
{
	if(system_flag_get(&boot_flag, BOOT_FLAG_IAP_XMODEMC) == true)
	{
		if(update_zone.Xmodem_timer >= 100)
		{
			update_zone.Xmodem_timer = 0;
			usart0_printf("C");
		}
		
		update_zone.Xmodem_timer++;
	}
	
	systick_delay_ms(10); 
}

/**
  * @func	MSR_SP
  * @param	addr	跳转指针
  * @return	none
  */
__asm void MSR_SP(uint32_t addr)
{
	MSR MSP, r0														/* 用MSR指令将r0的值写入MSP主堆栈指针 */
	BX r14															/* 返回调用MSR_SP函数的主函数 */
}

/**
  * @func	bootloader_jump_to_app
  * @param	app_addr	跳转指针
  * @return	none
  */
void bootloader_jump_to_app(uint32_t app_addr)
{
	jump_func jump_func_ptr;
	
	if((*(uint32_t *)app_addr >= 0x20000000) && (*(uint32_t *)app_addr <= 0x20004fff))
	{
		usart0_printf("[info] jump to zone A...\r\n");
		MSR_SP(*(uint32_t *)app_addr);								/* 设置栈顶指针 */
		jump_func_ptr = (jump_func)(*(uint32_t *)(app_addr + 4));	/* 跳转地址为A区的异常复位向量 */
		jump_func_ptr();											/* 跳转 */
	}
	else
	{
		system_flag_set(&boot_flag, BOOT_FLAG_APP_START_FAILED);
		usart0_printf("[info] jump to zone A failed!\a\r\n\r\n");
	}
}

/**
  * @func	bootloader_system_info
  * @param	none
  * @return	none
  */
void bootloader_system_info(void)
{
	usart0_printf("\r\n");
	usart0_printf("[info] CPU         %s\r\n", SYS_INFO_MCU_MODEL);
	usart0_printf("[info] Frequency   %s\r\n", SYS_INFO_MCU_Hz);
	usart0_printf("[info] SRAM        %s\r\n", SYS_INFO_MCU_SRAM);
	usart0_printf("[info] FLASH       %s\r\n", SYS_INFO_FLASH_SIZE);
	usart0_printf("[info] EEPROM      %s\r\n", SYS_INFO_EEPROM_SIZE);
	usart0_printf("[info] 4G          %s\r\n", SYS_INFO_NET_MODULE);
	usart0_printf("\r\n");
	usart0_printf("[info] Press ENTER to enter the command line!\r\n");
	usart0_printf("[info] countdown   ");
}

/**
  * @func	bootloader_cmdline_info
  * @param	none
  * @return	none
  */
void bootloader_cmdline_info(void)
{
	usart0_printf("\r\n");
	usart0_printf("[info] Welcome to bootloader command line! Press <num> to select command!\r\n");
	usart0_printf("      [1] Erase zone A!\r\n");
	usart0_printf("      [2] IAP download program to zone A!\r\n");
	usart0_printf("      [3] Bootloader version get!\r\n");
	usart0_printf("      [4] Bootloader version set!\r\n");
	usart0_printf("      [5] System reset!\r\n");
	usart0_printf("      [6] Server connect info set!\r\n");
}

/**
  * @func	bootloader_brance
  * @param	none
  * @return	none
  */
void bootloader_brance(void)
{
	uint8_t ret;
	
	ret = bootloader_cmdline_enter(10);
	if(!ret)
	{
		bootloader_ota_event_flag_read();
		
		if(ota_info.ota_event_flag == OTA_EVENT_FLAG_SET)
		{
			usart0_printf("[info] OTA event!\r\n");
		}
		else
		{
			bootloader_jump_to_app(GD_FLASH_A_BASE_ADDR);  
		}
	}

	bootloader_cmdline_info();
}

/**
  * @func	bootloader_brance
  * @param	timeout		秒倒计时
  * @return	0	不进入命令行		1	进入命令行
  */
uint8_t bootloader_cmdline_enter(uint8_t timeout)
{
	bootloader_system_info();
	
	while(timeout--)
	{
		usart0_printf("\b");
		usart0_printf("%d", timeout);
		
		systick_delay_ms(1000);	
		if(usart0_rx_buff[0] == '\r')
			return 1;
	}
	
	usart0_printf("\r\n");
	
	return 0;
}

/**
  * @func	bootloader_cmd_parser
  * @param	cmd_buff	命令指针
  * @return	none
  */
void bootloader_cmd_parser(char *cmd_buff)
{
	uint8_t i;
	
	boot_cmd_list.cmd_index = -1;
	for(i = 0; i < CMD_LIST_SIZE; i++)
	{
		if(!strcmp(cmd_buff, boot_cmd_list.boot_cmd[i].cmd_name))
		{
			boot_cmd_list.cmd_index = i;
			break;
		}
	}
}

/**
  * @func	bootloader_cmd_exec
  * @param	none
  * @return	0	success		-1	 failed
  */
int bootloader_cmd_exec(void)
{
	cmd_callback_func boot_cmd_callback;
	
	if(boot_cmd_list.cmd_index == -1)
		return -1;
	
	boot_cmd_callback = boot_cmd_list.boot_cmd[boot_cmd_list.cmd_index].cmd_callback;
	if(boot_cmd_callback != NULL)
	{
		boot_cmd_callback();
	}
	
	return 0;
}

/**
  * @func	bootloader_cmdline_loop
  * @param	none
  * @return	none
  */
void bootloader_cmdline_loop(void)
{
	char cmd_buff;
	
	if(system_flag_get(&boot_flag, BOOT_FLAG_APP_START_FAILED) == false)
	{
		system_flag_clr(&boot_flag, BOOT_FLAG_APP_START_FAILED);
		
		usart0_gets(&cmd_buff, sizeof(cmd_buff));
		memset(&cmd_buff, 0, sizeof(cmd_buff));
	}
	
	while(1)
	{
		usart0_printf("[info] ");
		usart0_gets(&cmd_buff, sizeof(cmd_buff));
		bootloader_cmd_parser(&cmd_buff);
		bootloader_cmd_exec();
	}
}

